use std::{env, io, fs};
use curl::easy::Easy;
use curl::Error;
use serde_json::Value;
use version_compare::{Version, CompOp};
use std::fs::File;
use std::process::Command;
use std::io::Write;
use std::os::raw::c_int;
use std::ffi::CString;
use std::ptr::null_mut;
use winapi::um::shellapi::ShellExecuteA;
use dialoguer::{Confirm};
use crate::prompt_theme::MyTheme;
use indicatif::{ProgressBar, ProgressStyle};
use std::path::{PathBuf, Path};
use winapi::um::winreg::HKEY_CURRENT_USER;
use winreg::RegKey;

/// 执行命令
/// operation: open,runas,print,edit,explore,find
/// 如果函数成功，则返回大于32的值。如果函数失败，则返回指示失败原因的错误值。返回值强制转换为HINSTANCE，以便与16位Windows应用程序向后兼容。
#[cfg(windows)]
pub fn exec(operation:&str, runexe:&str, command:&str, show_cmd:c_int) {
    // open、runas、print、edit、explore、find
    let operation = CString::new(operation).unwrap();
    let runexe = CString::new(runexe).unwrap();
    let command = CString::new(command).unwrap();
    const WINDOWS_SHELL_EXECUTE_SUCCESS: c_int = 32;
    let h_instance  = unsafe {
        ShellExecuteA(
            null_mut(),
            operation.as_ptr(),
            runexe.as_ptr(),
            command.as_ptr(),
            null_mut(),
            show_cmd) as c_int
    };

    if h_instance > WINDOWS_SHELL_EXECUTE_SUCCESS {
        println!("exec ok");
    }else {
        println!("exec error, error is: {}",h_instance);
    }

}

pub fn update(current_version: &str) {
    let url = "https://api.github.com/repos/fengzhongyun1992/doorer/releases/latest";
    let mut data_json = Vec::new();
    let mut handle = Easy::new();
    handle.follow_location(true).unwrap();
    handle.useragent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36").unwrap();
    handle.url(url).unwrap();
    {
        let mut transfer = handle.transfer();
        transfer.write_function(|new_data| {
            data_json.extend_from_slice(new_data);
            Ok(new_data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let data_json: Value = serde_json::from_slice(&data_json).unwrap();


    let last_version = data_json["tag_name"].as_str().unwrap();
    let browser_download_url = data_json["assets"][0]["browser_download_url"].as_str().unwrap();
    println!("last_version : {}", &last_version[1..]);
    let update_info = data_json["body"].as_str().unwrap();

    // 版本比较
    let crate_ver = Version::from(current_version).unwrap();
    let last_ver = Version::from(&last_version[1..]).unwrap();

    // 可以升级
    if let CompOp::Gt = last_ver.compare(&crate_ver) {
        println!("更新内容: \r\n {}",update_info);
        if Confirm::with_theme(&MyTheme::custom())
            .with_prompt("是否更新?")
            .default(true)
            .interact()
            .unwrap() {
            // 更新
            // 下载位置
            let mut download_path = env::temp_dir();
            download_path.push("doorer.exe");

            // 下载
            // 使用镜像下载
            let r = download(format!("{}{}","https://gh.api.99988866.xyz/",browser_download_url).as_str(),&download_path);

            // 下载失败 或者 文件大小
            if r.is_err() || fs::metadata(&download_path).unwrap().len() < 1024000 {
                println!("下载失败，使用github.com进行下载");
                download(browser_download_url,&download_path).unwrap();
            }

            if fs::metadata(&download_path).unwrap().len() < 1024000 {
                println!("文件大小不对，更新失败");
                return;
            }
            // 复制替换 本身
            // 等待 2秒 进行 自删除 复制等操作
            let bin_path = env::current_exe().unwrap();
            let bin_path = bin_path.to_str().unwrap();
            let temp_path = download_path.to_str().unwrap();
            let command = format!("choice /t 1 /d y /n >nul & del {} & move {} {} >nul",bin_path,temp_path,bin_path);
//            println!("{}",command);
            // 新建 子进程 进程 替换操作
            Command::new("cmd").arg("/C").arg(command).spawn().unwrap();
            println!("升级完成")
        }
    }else {
        println!("没有新版本需要更新")
    }
}


/// 文件下载函数
/// url 下载链接
/// target 下载位置
pub fn download(url :&str,target :&PathBuf) ->Result<(), Error>{
    println!("url : {}",url);
    let mut file = File::create(&target).unwrap();
    let mut handle = Easy::new();

    handle.useragent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36").unwrap();

    handle.url(url).unwrap();
    // 允许重定向
    handle.follow_location(true).unwrap();
    // 开启进度
    handle.progress(true).unwrap();

    // 进度条
    let pb = ProgressBar::new(0);

    pb.set_style(ProgressStyle::default_bar()
        .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
        .progress_chars("#>-"));

    {
        let mut transfer = handle.transfer();
        // 写入函数
        transfer.write_function(move |data| {
            file.write(&data).unwrap();
            Ok(data.len())
        }).unwrap();

        // 进度条更新函数
        transfer.progress_function( |a, b, _c, _d| {

            if a as u32 != 0 {
                // 设置长度
                pb.set_length(a as u64);
                // 设置进度
                pb.set_position(b as u64);
            }

            true
        }).unwrap();

        // 执行
        transfer.perform()?;
    }

    // 进度条结束
    pb.finish();
    Ok({})
}

/// 获取系统 代理地址
pub fn get_proxy_url() -> io::Result<String> {
    let hklm = RegKey::predef(HKEY_CURRENT_USER);
    let cur_ver = hklm.open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")?;
    // let enable : u32= cur_ver.get_value("ProxyEnable").unwrap();
    // println!("{}",enable);
    cur_ver.get_value("ProxyServer")
}

/// 清空文件夹
/// 但不删除文件夹自身
pub fn clean_dir<P : AsRef<Path>>(path: P) -> io::Result<()>{
    for entry in fs::read_dir(path)? {
        let entry = entry?;
        let path = entry.path();
        if entry.file_type()?.is_dir() {
            fs::remove_dir_all(path)?;
        }else{
            fs::remove_file(path)?
        }
    }
    Ok(())
}


pub struct MyData<'a> {
    pub names: Vec<&'a str>,
    pub values: Vec<&'a str>
}

impl<'a> MyData<'a> {
    pub fn new() -> MyData<'a> {
        MyData{
            names: vec![],
            values: vec![]
        }
    }

    pub fn add(&mut self,name:&'a str,value:&'a str) {
        self.values.push(value);
        self.names.push(name);
    }
}